package com.zz.admin.menuManagement.service.impl;

import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.zz.admin.common.auth.AuthUtils;
import com.zz.admin.menuManagement.dto.MenuDto;
import com.zz.admin.menuManagement.entity.Menu;
import com.zz.admin.menuManagement.mapper.MenuMapper;
import com.zz.admin.menuManagement.service.MenuService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zz.common.utils.cache.LocalCacheUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import com.zz.common.base.dto.PageListResultDto;
import com.zz.common.base.param.QueryParam;
import com.zz.common.base.enums.BaseExceptionEnum;
import com.zz.common.constants.BaseConstant;
import com.zz.common.utils.mapper.BeanMapper;
import com.zz.common.utils.common.UUIDGenerator;
import com.zz.common.exception.ExceptionFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;


/**
 * 菜单表 服务实现类
 * @author zhang.lei
 * @since 2025-04-12 15:32:26
 */
@Service
@Slf4j
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    private final String CACHE_PREFIX1 = "allMenu:";

    @Resource
    private MenuMapper menuMapper;

    @Override
    public PageListResultDto<MenuDto> queryWithPage(MenuDto menuDto, QueryParam queryParam) {
        Page<Menu> page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize());
        QueryWrapper<Menu> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", BaseConstant.NO);
        List<Menu> menus = menuMapper.selectList(page, wrapper);
        List<MenuDto> menuDtos = BeanMapper.mapList(menus, Menu.class, MenuDto.class);
        return new PageListResultDto<>((int) page.getCurrent(), (int) page.getTotal(), queryParam.getPageSize(), menuDtos);
    }

    @Override
    public List<MenuDto> queryWithNoPage(MenuDto menuDto) {
        QueryWrapper<Menu> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", BaseConstant.NO);
        List<Menu> menus = menuMapper.selectList(wrapper);
        return BeanMapper.mapList(menus, Menu.class, MenuDto.class);
    }

    @Override
    public MenuDto queryById(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        Menu menu = menuMapper.selectById(id);
        if (menu == null || BaseConstant.YES.equals(menu.getDelFlag())) {
            return null;
        }
        return BeanMapper.map(menu, MenuDto.class);
    }

    @Override
    public List<MenuDto> queryByIds(List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }
        List<Menu> menus = menuMapper.selectByIds(ids);
        if (CollectionUtils.isEmpty(menus)) {
            return new ArrayList<>();
        }
        return BeanMapper.mapList(menus, Menu.class, MenuDto.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public MenuDto save(MenuDto menuDto) {
        // 根据业务修改
        return insert(menuDto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<MenuDto> saveBatch(List<MenuDto> menuDtos) {
        // 根据业务修改
        return insertList(menuDtos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public MenuDto modify(MenuDto menuDto) {
        // 根据业务修改
        updateById(menuDto);
        return menuDto;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<MenuDto> modifyBatch(List<MenuDto> menuDtos) {
        // 根据业务修改
        return updateList(menuDtos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String id) {
        // 根据业务修改
        MenuDto menuDto = new MenuDto();
        menuDto.setMenuId(id);
        deleteById(menuDto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBatch(List<String> ids) {
        // 根据业务修改
        deleteByPrimaryKeys(ids);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public MenuDto insert(MenuDto menuDto) {
        menuDto.setMenuId(UUIDGenerator.generateUUID());
        menuDto.setDelFlag(BaseConstant.NO);
        Menu entity = BeanMapper.map(menuDto, Menu. class);
        menuMapper.insert(entity);
        return menuDto;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public MenuDto insertWithId(MenuDto menuDto) {
        if (StringUtils.isBlank(menuDto.getMenuId())) {
            menuDto.setMenuId(UUIDGenerator.generateUUID());
        }
        menuDto.setDelFlag(BaseConstant.NO);
        Menu entity = BeanMapper.map(menuDto, Menu.class);
        menuMapper.insert(entity);
        return menuDto;
    }

    private List<MenuDto> insertList(List<MenuDto> menuDtos) {
        if (CollectionUtils.isEmpty(menuDtos)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量插入时，参数不能为空");
        }
        List<Menu> insertList = new ArrayList<>();
        for (MenuDto menuDto : menuDtos) {
            if (StringUtils.isBlank(menuDto.getMenuId())) {
                menuDto.setMenuId(UUIDGenerator.generateUUID());
            }
            menuDto.setDelFlag(BaseConstant.NO);
            insertList.add(BeanMapper.map(menuDto, Menu.class));
        }
        menuMapper.insert(insertList, 1000);
        return menuDtos;
    }

    private List<MenuDto> updateList(List<MenuDto> menuDtos) {
        if (CollectionUtils.isEmpty(menuDtos)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量更新时，参数不能为空");
        }
        for (MenuDto menuDto : menuDtos) {
            if (StringUtils.isBlank(menuDto.getMenuId())) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量更新时，主键不能为空");
            }
        }
        List<Menu> menuList = BeanMapper.mapList(menuDtos, MenuDto.class, Menu.class);
        menuMapper.updateById(menuList, 1000);
        return menuDtos;
    }

    @Transactional(rollbackFor = Exception.class)
    public int updateById(MenuDto menuDto) {
        if (StringUtils.isBlank(menuDto.getMenuId())) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "更新时，主键不能为空");
        }
        Menu entity = BeanMapper.map(menuDto, Menu. class);
        return menuMapper.updateById(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    public int trueDeleteById(String id) {
        return menuMapper.deleteById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public int deleteById(MenuDto menuDto) {
        menuDto.setDelFlag(BaseConstant.YES);
        return updateById(menuDto);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteByPrimaryKeys(List<String> ids) {
        List<MenuDto> menuDtos = new ArrayList<>();
        for (String id : ids) {
            MenuDto menuDto = new MenuDto();
            menuDto.setMenuId(id);
            menuDto.setDelFlag(BaseConstant.YES);
            menuDtos.add(menuDto);
        }
        updateList(menuDtos);
    }

    @Override
    public List<MenuDto> queryTreeRightMenus(String userId) {
        Set<String> menuIds = getRightMenuIds(null);
        // 上面的menuIds是有权限的菜单ids
        List<MenuDto> rightMenus = findAll().stream()
                .filter(e -> menuIds.contains(e.getMenuId()))
                .distinct()
                .toList();

        List<MenuDto> pMenus = rightMenus.stream().filter(e -> StringUtils.isBlank(e.getParentId()))
                .collect(Collectors.toList());

        // 将过滤得到的集合转化为树状结构
        return toArrayTree(pMenus, menuIds);
    }

    /**
     * 得到树状结构数据
     *
     * @param pMenus
     * @return
     */
    private List<MenuDto> toArrayTree(List<MenuDto> pMenus, Set<String> menuIds) {
        List<MenuDto> result = new ArrayList<>();

        // 按displaySeq排序
//        Collections.sort(pMenus, Comparator.comparingInt(MenuDto::getDisplaySeq));
        pMenus.sort(Comparator.comparingInt(item -> item.getDisplaySeq() != null ? item.getDisplaySeq() : Integer.MAX_VALUE));

        if (CollectionUtils.isNotEmpty(pMenus)) {
            pMenus.forEach(item -> {
                if (menuIds.contains(item.getMenuId())) {
                    // 子级
                    List<MenuDto> cMenus = queryByParentId(item.getMenuId());
                    if (CollectionUtils.isNotEmpty(cMenus)) {
                        item.setChildren(toArrayTree(cMenus, menuIds));
                    }
                    item.setName(item.getMenuName());
                    result.add(item);
                }
            });
        }

        return result;
    }

    /**
     * 根据父级id查询子节点
     *
     * @param pId
     * @return
     */
    public List<MenuDto> queryByParentId(String pId) {
        if (org.apache.commons.lang3.StringUtils.isBlank(pId)) {
            return new ArrayList<>();
        }
        List<MenuDto> rightMenus = findAll();
        List<MenuDto> collect = rightMenus.stream()
                .filter(e -> pId.equals(e.getParentId()) && e.getDelFlag().equals(BaseConstant.NO))
                .collect(Collectors.toList());
        return collect;
    }
    
    // 得到当前登录人有权限查看的menuIds(去重)
    private Set<String> getRightMenuIds(String userId) {
        if (StringUtils.isBlank(userId)) {
            userId = AuthUtils.getCurrUser().getUserId();
        }
        // 通过userId => 找到roleIds(利用auth_user_role_t)
        // 通过roleIds => 找到menus(利用auth_role_menu_t)
        return menuMapper.getRightMenuIds(userId);
    }

    private List<MenuDto> findAll() {
        long begin = System.currentTimeMillis();

        String menusStr = (String) LocalCacheUtil.get(CACHE_PREFIX1);

        if (StringUtils.isNotBlank(menusStr)) {
            List<MenuDto> menuDtos = JSON.parseArray(menusStr, MenuDto.class);
            long end = System.currentTimeMillis() - begin;
            log.info("--------------------获取缓存中的全部菜单耗时：" + end + "毫秒--------------------");
            return menuDtos;
        }

        return saveAllCache();
    }

    public List<MenuDto> saveAllCache() {
        long begin = System.currentTimeMillis();
        log.info("------------------未使用缓存中的菜单信息------------------");

        List<MenuDto> finalMenuDtoList;

        QueryWrapper<Menu> menuWrapper = new QueryWrapper<>();
        menuWrapper.eq("del_flag", BaseConstant.NO);
        List<Menu> menus = menuMapper.selectList(menuWrapper);

        finalMenuDtoList = BeanMapper.mapList(menus, Menu.class, MenuDto.class);
        if (CollectionUtils.isEmpty(finalMenuDtoList)) {
            finalMenuDtoList = new ArrayList<>();
        }

        LocalCacheUtil.put(CACHE_PREFIX1, JSON.toJSONString(finalMenuDtoList), 500);

        long end = System.currentTimeMillis() - begin;
        log.info("------------------添加菜单缓存所用耗时" + end + ":毫秒----------" + "缓存菜单数量:{}", finalMenuDtoList.size());

        return finalMenuDtoList;
    }
}
